package com.gitee.wsl.common.ui.input

import com.gitee.wsl.common.ui.base.Point
import com.gitee.wsl.common.ui.input.event.EventPropagation
import com.gitee.wsl.common.ui.input.event.KDragEvent
import com.gitee.wsl.common.ui.input.event.KEvent
import com.gitee.wsl.common.ui.input.event.KPointerEvent
import com.gitee.wsl.common.ui.input.event.KPointerEvents

/**
 * A [KEventListener] is an interface for handling a <T> KEvent on any given target.
 */
fun interface KEventListener<T> where  T : KEvent {

    /**
     * Add a listener on any target for a given <T> KEvent.
     * In return the listener must return a [EventPropagation] to indicate if the base event continues back
     * to the platform or stops here and is not propagated.
     */
    fun addNativeListener(target: Any, listener: (T) -> EventPropagation): Disposable
}

fun <T: KEvent> emptyListener()= KEventListener<T> { _, _ -> Disposable{} }


internal fun dragEventsListener(): KEventListener<KDragEvent> = object : KEventListener<KDragEvent> {

    private var downActionPos: Point? = null
    private var dragInProgress: Boolean = false

    override fun addNativeListener(target: Any, listener: (KDragEvent) -> EventPropagation): Disposable {

        val compositeDisposable = CompositeDisposable()

        compositeDisposable.add(KPointerEvents.move.addNativeListener(target) {
            if (dragInProgress) {
                listener(KDragEvent(KDragEvent.KDragAction.Dragging, it))
            } else {
                val startPos = downActionPos
                if (startPos != null) {
                    dragInProgress = true
                    listener(KDragEvent(KDragEvent.KDragAction.Start, it))
                }
                EventPropagation.Continue
            }
        })

        compositeDisposable.add(KPointerEvents.leave.addNativeListener(target) {
            onDragNotPossible(listener, it)
        })

        compositeDisposable.add(KPointerEvents.down.addNativeListener(target) {
            downActionPos = it.pos
            EventPropagation.Continue
        })

        compositeDisposable.add(KPointerEvents.up.addNativeListener(target) {
            onDragNotPossible(listener, it)
        })

        return compositeDisposable
    }

    private fun onDragNotPossible(listener: (KDragEvent) -> EventPropagation, motionEvent: KPointerEvent): EventPropagation {
        downActionPos = null
        return if (dragInProgress) {
            dragInProgress = false
            listener(KDragEvent(KDragEvent.KDragAction.Finish, motionEvent))
        } else {
            EventPropagation.Continue
        }
    }

}